<html>
<head>
<script src="../../resources/js-test.js"></script>
</head>
<body>
<script>
description("Tests various use cases when cloning MessagePorts.");
window.jsTestIsAsync = true;

var channel = new MessageChannel;
channel.port1.onmessage = channel.port2.onmessage = function(evt) {
    testFailed("Should not have received message: " + evt.data);
}

// Posting port to itself should throw an exception.
shouldThrow("channel.port1.postMessage('msg', [channel.port1])", '"DataCloneError: Failed to execute \'postMessage\' on \'MessagePort\': Port at index 0 contains the source port."');

debug("Posting port to entangled pair neuters the port and does nothing else:");
channel = new MessageChannel;
var channel2 = new MessageChannel;
channel.port1.postMessage("msg", [channel.port2]);
shouldThrow("channel2.port1.postMessage('msg', [channel.port2])", '"DataCloneError: Failed to execute \'postMessage\' on \'MessagePort\': Port at index 0 is already neutered."');

channel = new MessageChannel;
channel2 = new MessageChannel
channel.port1.postMessage("msg", [channel2.port1]);

debug("Posting a detached ArrayBuffer should throw.");
channel = new MessageChannel;
var arrayBuffer = new ArrayBuffer(2);

channel.port1.postMessage("msg", [arrayBuffer]);
shouldThrow("channel.port1.postMessage(arrayBuffer, [])", '"DataCloneError: Failed to execute \'postMessage\' on \'MessagePort\': An ArrayBuffer is detached and could not be cloned."');

debug("Posting a detached ArrayBufferView should throw.");
channel = new MessageChannel;
var uint16Array = new Uint16Array(10);

shouldThrow("channel.port1.postMessage('msg', [uint16Array])", '"DataCloneError: Failed to execute \'postMessage\' on \'MessagePort\': Value at index 0 does not have a transferable type."');

channel.port1.postMessage("msg", [uint16Array.buffer]);
shouldThrow("channel.port1.postMessage(uint16Array, [])", '"DataCloneError: Failed to execute \'postMessage\' on \'MessagePort\': An ArrayBuffer is detached and could not be cloned."');

// Should not be able to post a cloned port.
shouldThrow("channel.port1.postMessage('msg', [channel2.port1])", '"DataCloneError: Failed to execute \'postMessage\' on \'MessagePort\': Port at index 0 is already neutered."');

// Test posting messages to a port in cloned state.

var channel = new MessageChannel;
var channel2 = new MessageChannel;

// Post messages before and after clone to make sure ordering is preserved and all messages are received.
channel2.port2.postMessage("1");
channel.port1.postMessage("msg", [channel2.port1]);
channel2.port2.postMessage("2");
channel2.port2.postMessage("3");
var testEvent;
var messageIndex = 1;
channel.port2.onmessage = function(evt) {
    testEvent = evt;
    shouldBe("testEvent.ports.length", "1");
    evt.ports[0].onmessage = function(evt) {
        testEvent = evt;
        shouldBe("parseInt(testEvent.data)", "" + messageIndex);
        messageIndex++;
        if (messageIndex == 4) {
            testPassed("Posted messages to cloned port.");
            testDoublyClonedPort();
        }
    }
}

function testDoublyClonedPort()
{
    var channel = new MessageChannel;
    var channel2 = new MessageChannel;
    channel.port1.postMessage("msg", [channel2.port1]);
    channel.port2.postMessage("msg", [channel2.port2]);
    gc();
    channel.port1.onmessage = function(evt) {
        evt.ports[0].postMessage("testme");
    }
    channel.port2.onmessage = function(evt) {
        evt.ports[0].onmessage = function (evt) {
            testEvent = evt;
            shouldBe("testEvent.data", "'testme'");
            testPostClosePort();
        }
    }

}

// *Should* be able to post a closed port.
function testPostClosePort()
{
    var channel = new MessageChannel;
    var channel2 = new MessageChannel;
    channel2.port2.close();
    channel.port1.postMessage("closed", [channel2.port2]);
    channel.port2.onmessage = function(evt) {
        testEvent = evt;
        shouldNotBe("testEvent.ports", "null");
        shouldBe("testEvent.ports.length", "1");
        shouldBe("testEvent.data", "'closed'");

        finishJSTest();
    }
}

</script>
</body>
</html>
